/*
*
* Copyright 2013 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package com.netflix.client;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.netflix.client.config.IClientConfig;
import com.netflix.config.ConfigurationManager;

/**
 * A "VipAddress" in Ribbon terminology(术语) is a logical name used for a target
 * server farm.
 * ribbon 术语中的“VipAddress”是表示目标服务器对于的 server 逻辑名称。
 *
 * This class helps interpret and resolve a "macro（宏）" and obtain a finalized vipAddress.
 * 此实现类将解释和解析“宏”，并获取最终的 vipaddress
 *
 * <p>
 * Ribbon supports a comma（逗号） separated set of logcial addresses for a Ribbon Client.
 * Ribbon 支持使用 ，分割多个逻辑地址的集合
 *
 *  Typical/default implementation uses the list of servers（对应配置是 listOfServers） obtained from
 * the first of the comma separated list and progresses down the list only when
 * the prior(先前的) vipAddress contains no servers.
 * <p>
 * This class assumes that the vip address string may contain marcos in the format 
 * of ${foo}, where foo is a property in Archaius configuration, and tries to replace 
 * these macros with actual values.
 * 
 * <p>
 * e.g. vipAddress settings
 * 
 * <code>
 * ${foo}.bar:${port},${foobar}:80,localhost:8080
 * 
 * The above list will be resolved by this class as 
 * 
 * apple.bar:80,limebar:80,localhost:8080
 * 
 * provided that the Configuration library resolves the property foo=apple,port=80 and foobar=limebar
 * 
 * </code>
 * 
 * @author stonse
 * 
 */
public class SimpleVipAddressResolver implements VipAddressResolver {

    /**
     * 正则表达式，表示变量的模板 ${}
     * 正则表达式：
     *   . 表示匹配除换行符 \n 之外的任何单字符。要匹配 . ，请使用 \.
     *   * 表示匹配前面的子表达式零次或多次。要匹配 * 字符，请使用 \*
     *   ？ 表示匹配前面的子表达式零次或一次，或指明一个非贪婪限定符。要匹配 ? 字符，请使用 \?
     */
    private static final Pattern VAR_PATTERN = Pattern.compile("\\$\\{(.*?)\\}");

    /**
     * Resolve the vip address by replacing macros with actual values in configuration.
     * 解析 vip address 并从 cofinguration 中替换响应的 macro 的值
     *
     * If there is no macro, the passed in string will be returned.
     * 如果没有设置 marco，直接返回字符串
     *
     *
     * If a macro is found but there is no property defined in configuration,
     * the same macro is returned as part of the result.
     * 如果配置了 marco，但 configuration 中却找不到对应值，则将 macro 作为地址的一部分返回
     *
     */
    @Override
    public String resolve(String vipAddressMacro, IClientConfig niwsClientConfig) {
        // vipAddressMacro 表示带有 macro 的 vipAddress
        if (vipAddressMacro == null || vipAddressMacro.length() == 0) {
            return vipAddressMacro;
        }
        // 替换 macro 的值
        return replaceMacrosFromConfig(vipAddressMacro);
    }
    
    private static String replaceMacrosFromConfig(String macro) {
        String result = macro;
        Matcher matcher = VAR_PATTERN.matcher(result);
        while (matcher.find()) {
            String key = matcher.group(1);
            // 从 archaius 中读取配置信息，因为需要依赖 archaius 所以此类放在 ribbon-archaius 项目中
            String value = ConfigurationManager.getConfigInstance().getString(key);
            if (value != null) {
                result = result.replaceAll("\\$\\{" + key + "\\}", value);
                // 再次匹配，若不存在，则跳出循环
                matcher = VAR_PATTERN.matcher(result);
            }
        }
        // 返回最终值，如果上面的流程没找到对应值，这里返回的还是源生的 macro，即 ${xxx}
        return result.trim();        
    }

}
